/*
 * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

use anyhow::{Context, Result};
use chrono::{Datelike, Timelike};
use env_logger::Env;
use glob::glob;
use log::{debug, warn};

use rpassword::read_password;
use std::fs::{self, File};
use std::io::BufRead;
use std::io::IsTerminal;
use std::io::{Read, Seek, Write};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use walkdir::WalkDir;

use crate::cli;
use crate::error::ZipError;
use crate::zip::{
    CompressionMethod, FileOptions, LineEndingConversion, ZipArchive, ZipFile, ZipWriter,
};

use crate::utils::logfile::LogFile;

// 跨文件系统安全的文件移动函数
pub fn safe_move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
    let from = from.as_ref();
    let to = to.as_ref();

    // 首先尝试快速的 rename 操作
    match fs::rename(from, to) {
        Ok(()) => {
            log::debug!(
                "Successfully renamed {} to {}",
                from.display(),
                to.display()
            );
            Ok(())
        }
        Err(e) => {
            // 如果是跨设备错误（EXDEV），使用复制+删除的方式
            if e.raw_os_error() == Some(18) {
                // EXDEV: Invalid cross-device link
                log::info!(
                    "Cross-device operation detected, falling back to copy+delete: {} -> {}",
                    from.display(),
                    to.display()
                );

                // 使用复制+删除的方式
                match fs::copy(from, to) {
                    Ok(_) => {
                        // 复制成功，删除原文件
                        match fs::remove_file(from) {
                            Ok(()) => {
                                log::debug!(
                                    "Successfully copied and removed: {} -> {}",
                                    from.display(),
                                    to.display()
                                );
                                Ok(())
                            }
                            Err(remove_err) => {
                                // 删除失败，清理已复制的文件
                                let _ = fs::remove_file(to);
                                Err(anyhow::anyhow!(
                                    "Failed to remove source file after copy: {} ({})",
                                    from.display(),
                                    remove_err
                                ))
                            }
                        }
                    }
                    Err(copy_err) => Err(anyhow::anyhow!(
                        "Failed to copy file {} to {}: {}",
                        from.display(),
                        to.display(),
                        copy_err
                    )),
                }
            } else {
                // 其他类型的错误，直接返回
                Err(anyhow::anyhow!(
                    "Failed to move file {} to {}: {}",
                    from.display(),
                    to.display(),
                    e
                ))
            }
        }
    }
}

// 生成类似标准zip工具的随机临时文件名
fn generate_temp_filename() -> String {
    use std::time::{SystemTime, UNIX_EPOCH};

    // 使用时间戳和进程ID来生成更加随机的文件名
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default();
    let pid = std::process::id();
    let timestamp_part = (now.as_nanos() % 0xFFFFFF) as u32;

    // 生成类似 ziABC123 这样的文件名，类似标准zip工具
    format!("zi{:06X}", (pid ^ timestamp_part) & 0xFFFFFF)
}

// 添加新的结构体来跟踪压缩信息
pub struct FileCompressionTracker {
    pub original_size: u64,
    pub compressed_size: u64,
    pub ratio: f64,
    pub method: CompressionMethod,
    #[allow(dead_code)]
    pub disk_num: u16,
}

#[derive(Default)]
pub struct RunState<'a> {
    pub zip_file: Option<PathBuf>,
    pub zip_file_tmp: Option<PathBuf>,
    pub writer: Option<ZipWriter<'a>>,
    pub archive: Option<ZipArchive>,
    pub file_options: FileOptions,
    pub dirs_to_remove: std::collections::HashSet<PathBuf>, // 待删除的目录

    pub total_original_size: u64,
    pub total_compressed_size: u64,
    pub total_entries: usize, // 统计文件数量

    pub changed_files: Vec<String>, // 保存已修改的文件列表

    pub update_modify_time: bool, // 是否更新修改时间

    pub testing: bool, // 启用测试模式

    pub verbose: bool,    // 启用详细输出
    pub quiet: bool,      // 启用安静模式
    pub show_debug: bool, // 启用调试模式 (--sd)

    output: Option<PathBuf>, // 输出文件路径

    pub log_file: Option<LogFile>, // 日志文件

    // 显示输出控制
    pub display_bytes: bool,        // --db
    pub display_count: bool,        // --dc
    pub display_dots: bool,         // --dd
    pub display_global_dots: bool,  // --dg
    pub dot_size: u64,              // --ds
    pub display_uncompressed: bool, // --du
    pub display_volume: bool,       // --dv

    pub disk_num: u16,
    pub changed_files_count: u16,
    pub changed_files_size: u64,
    last_changed_file_size: u64,
    pub changed_files_total_size: u64,
    pub changed_files_total_count: u16,

    pub args: cli::ZipArgs,

    global_bytes_processed: u64,
    #[allow(dead_code)]
    global_dots_shown: u64,
}

// 手动实现Debug，跳过writer和archive字段
impl<'a> std::fmt::Debug for RunState<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RunState")
            .field("zip_file", &self.zip_file)
            .field("zip_file_tmp", &self.zip_file_tmp)
            .field("file_options", &self.file_options)
            .field("dirs_to_remove", &self.dirs_to_remove)
            .field("total_original_size", &self.total_original_size)
            .field("total_compressed_size", &self.total_compressed_size)
            .field("changed_files", &self.changed_files)
            .field("update_modify_time", &self.update_modify_time)
            .field("testing", &self.testing)
            .field("verbose", &self.verbose)
            .field("quiet", &self.quiet)
            .finish()
    }
}

impl<'a> RunState<'a> {
    pub fn new(zipfile: Option<PathBuf>) -> Self {
        // 初始化RunState
        let zip_file = zipfile;
        let zip_file_tmp = None; // 临时文件路径将在init_run_state中根据-b参数设置
        Self {
            zip_file,
            zip_file_tmp,
            writer: None,
            archive: None,
            file_options: FileOptions::new(),
            dirs_to_remove: std::collections::HashSet::new(),
            changed_files: Vec::new(),
            output: None,
            log_file: None,
            args: cli::ZipArgs::default(),
            disk_num: 1,
            ..Default::default()
        }
    }

    // 完成操作并清理资源
    pub fn finish(&mut self) -> Result<()> {
        let mut max_time = None;

        let mut split_name = None;
        if let Some(mut writer) = self.writer.take() {
            split_name = writer.get_split_name();
            if self.update_modify_time {
                let archive = writer.finish_into_readable()?;
                for file in archive.files() {
                    let modify_time = file.last_modified()?;
                    if max_time.is_none() || modify_time > max_time.unwrap() {
                        max_time = Some(modify_time);
                    }
                }
            } else {
                writer.finish()?;
            }
        }

        if let Some(output) = &self.output {
            // 如果指定了输出文件名，则重命名
            log::debug!("Output file specified: {}", output.display());
            self.zip_file = Some(output.clone());
        }

        if self.zip_file.as_ref().unwrap().exists() {
            fs::remove_file(self.zip_file.as_ref().unwrap()).with_context(|| {
                format!(
                    "Failed to remove existing zip file: {}",
                    self.zip_file.as_ref().unwrap().display()
                )
            })?;
        }

        // testing 字段在这里不再使用，因为测试逻辑已移到各个命令中处理

        // 存在跨卷的情况，需要重新命名最后一个压缩包
        let final_file_path = if let Some(splitname) = split_name {
            // 检查是否是单个分割文件（.z01扩展名）
            if splitname.ends_with(".z01") {
                // 单个分割文件，保持.z01扩展名，不重命名
                log::info!("Single split file, keeping .z01 extension: {}", splitname);
                PathBuf::from(splitname)
            } else {
                // 多个分割文件，最后一个重命名为.zip
                safe_move_file(&splitname, self.zip_file.as_ref().unwrap())?;
                self.zip_file.as_ref().unwrap().clone()
            }
        } else {
            safe_move_file(
                self.zip_file_tmp.as_ref().unwrap(),
                self.zip_file.as_ref().unwrap(),
            )?;
            self.zip_file.as_ref().unwrap().clone()
        };

        // 设置最终文件的修改时间为找到的最大时间
        if let Some(max_time) = max_time {
            let system_time: SystemTime = max_time.into();
            filetime::set_file_mtime(
                &final_file_path,
                filetime::FileTime::from_system_time(system_time),
            )?;
        }

        if let Some(log_file) = self.log_file.as_mut() {
            log_file.close()?;
        }

        Ok(())
    }

    pub fn set_display_info(&mut self, args: &crate::cli::ZipArgs) {
        self.display_bytes = args.display.display_bytes;
        self.display_count = args.display.display_count;
        self.display_dots = args.display.display_dots;
        self.display_global_dots = args.display.display_dots_global;
        self.dot_size = args.display.display_dots_size.unwrap_or(0);
        self.display_uncompressed = args.display.display_uncompressed;
        self.display_volume = args.display.display_volume;
        self.show_debug = args.other.show_debug;

        // 如果设置了dot_size但没有显式启用点数显示，则自动启用默认点数显示（与原生zip行为一致）
        if self.dot_size > 0 && !self.display_dots && !self.display_global_dots {
            self.display_dots = true;
        }
    }

    /// 输出调试信息 (--sd)
    pub fn debug_print(&self, message: &str) {
        if self.show_debug {
            println!("sd: {}", message);
        }
    }

    // 打印操作结束信息, 包含原始大小，压缩大小，压缩方法
    pub fn print_operation_end_args(
        &mut self,
        original_size: u64,
        compressed_size: u64,
        method: CompressionMethod,
    ) {
        let tracker = FileCompressionTracker {
            original_size,
            compressed_size,
            method,
            ratio: caculate_ratio(original_size, compressed_size),
            disk_num: 1,
        };
        self.print_operation_end(&tracker);
    }
    // 打印操作结束信息
    pub fn print_operation_end(&mut self, tracker: &FileCompressionTracker) {
        let origin_size_formt = format!(" ({}) ", format_size(tracker.original_size, 1));

        let size_format = format!(
            " (in={}) (out={}) ",
            tracker.original_size, tracker.compressed_size,
        );
        let ratio_format = format!(" ({} {:.0}%)", tracker.method, tracker.ratio);
        if let Some(log_file) = self.log_file.as_mut() {
            if self.verbose {
                log_file.write_log(&size_format, None).unwrap();
            }
            log_file.write_log(&ratio_format, Some(())).unwrap();
        }
        if !self.quiet {
            if self.display_uncompressed {
                print!("{}", origin_size_formt);
            }

            if self.verbose && self.args.command != cli::Command::Delete {
                print!("{}", size_format);
            }

            if self.args.command == cli::Command::Delete {
                println!();
            } else {
                println!("{}", ratio_format);
            }
        }
    }

    pub fn update_display_info<S>(&mut self, disk_num: u16, size_or_path: S)
    where
        S: SizeProvider,
    {
        self.disk_num = disk_num;
        self.changed_files_count += 1;

        let changed_files_size = size_or_path.get_size();

        self.changed_files_size += changed_files_size;
        self.last_changed_file_size = changed_files_size;
    }

    // 打印操作开始信息
    pub fn print_operation_start(&mut self, action: &str, name: &str) -> Result<()> {
        let disk_num_format = format!("1>{}: ", self.disk_num);
        let mut change_count_format = format!(
            "{:>3}/{:>3} ",
            self.changed_files_count.saturating_sub(1),
            self.changed_files_total_count
                .saturating_sub(self.changed_files_count.saturating_sub(1))
        );
        let mut change_size_format = format!(
            "[{:>4}/{:>4}] ",
            format_size(
                self.changed_files_size
                    .saturating_sub(self.last_changed_file_size),
                1
            ),
            format_size(
                self.changed_files_total_size.saturating_sub(
                    self.changed_files_size
                        .saturating_sub(self.last_changed_file_size)
                ),
                1
            ),
        );
        let action_format = format!("  {}: {} ", action, name);
        if !self.quiet {
            if self.display_volume {
                print!("{}", disk_num_format);
            }
            if self.display_count {
                if self.args.basic_mode_options.filesync && action == "deleting" {
                    change_count_format = format!("{:>3}/{:>3} ", "", "");
                }
                print!("{}", change_count_format);
            }
            if self.display_bytes {
                if self.args.basic_mode_options.filesync && action == "deleting" {
                    change_size_format = format!("{:>5}/{:>5} ", "", "");
                }
                print!("{}", change_size_format);
            }

            print!("{}", action_format);
        }
        if let Some(log_file) = self.log_file.as_mut() {
            if self.display_volume {
                log_file.write_log(&disk_num_format, None)?;
            }
            if self.display_count {
                log_file.write_log(&change_count_format, None)?;
            }
            if self.display_bytes {
                log_file.write_log(&change_size_format, None)?;
            }

            log_file.write_log(&action_format, None)?;

            if action == "deleting" {
                log_file.write_log("", Some(()))?;
            }
        }
        std::io::stdout().flush()?;
        Ok(())
    }

    pub fn print_summary_verbose(&mut self) {
        let summary_format = format!(
            "total bytes={}, compressed={} -> {:.0}% savings",
            self.total_original_size,
            self.total_compressed_size,
            caculate_ratio(self.total_original_size, self.total_compressed_size),
        );
        if self.verbose {
            println!("{}", summary_format);
        }
        if let Some(log_file) = self.log_file.as_mut() {
            log_file.write_log(&summary_format, Some(())).unwrap();
        }
    }

    // 初始化显示信息，计算需要操作的文件数量，和大小
    pub fn init_display_info(
        &mut self,
        select_files: &std::collections::BTreeMap<String, PathBuf>,
        command: &cli::Command,
    ) -> Result<()> {
        match command {
            cli::Command::Add => {
                for path in select_files.values() {
                    let file_size = path
                        .metadata()
                        .map(|m| if path.is_dir() { 0 } else { m.len() })
                        .unwrap_or(0);

                    self.changed_files_total_size += file_size;
                    self.changed_files_total_count += 1;
                }
            }
            cli::Command::Update => {
                let archive = self
                    .archive
                    .as_mut()
                    .ok_or_else(|| anyhow::anyhow!("Archive is not initialized"))?;

                for (name, path) in select_files {
                    // 检查是否是特殊标记路径
                    let path_str = path.to_string_lossy();
                    if path_str.starts_with("__ZIP_ENTRY__:") {
                        // 对于特殊标记路径，我们假设需要更新（实际逻辑在process_existing_files中处理）
                        self.changed_files_total_count += 1;
                        continue;
                    }

                    let file_size = path
                        .metadata()
                        .map(|m| if path.is_dir() { 0 } else { m.len() })
                        .unwrap_or(0);

                    let file = match archive.by_name(name) {
                        Ok(f) => f,
                        Err(e) => {
                            debug!("{} not in archive: {}", name, e);
                            self.changed_files_total_size += file_size;
                            self.changed_files_total_count += 1;
                            continue;
                        }
                    };
                    let should_update = should_update_file(&file, path)?;
                    if should_update {
                        self.changed_files_total_size += file_size;
                        self.changed_files_total_count += 1;
                    }
                }
            }
            cli::Command::Delete => {
                let archive = self
                    .archive
                    .as_mut()
                    .ok_or_else(|| anyhow::anyhow!("Archive is not initialized"))?;
                let files_to_delete = select_files.keys().collect::<Vec<_>>();
                for name in archive.file_names() {
                    let should_delete = files_to_delete
                        .iter()
                        .any(|pattern| match_pattern(&name, pattern, self.args.other.no_wildcards));

                    if should_delete {
                        if apply_filters(&name, &self.args, true) {
                            continue;
                        }

                        let file = match archive.by_name(&name) {
                            Ok(f) => f,
                            Err(e) => {
                                debug!("{} not in archive: {}", name, e);
                                continue;
                            }
                        };
                        self.changed_files_total_size += file.compressed_size();
                        self.changed_files_total_count += 1;
                    }
                }
            }

            &cli::Command::Copy => {
                let files_to_copy = select_files.keys().collect::<Vec<_>>();
                let archive = self
                    .archive
                    .as_mut()
                    .ok_or_else(|| anyhow::anyhow!("Archive is not initialized"))?;

                // 没有选择，则使用压缩的所有文件复制
                if files_to_copy.is_empty() {
                    self.changed_files_total_count = archive.len() as u16;
                    self.changed_files_total_size = archive.get_total_compressed_size();
                    return Ok(());
                }
                let all_names = archive.file_names();

                for name in all_names {
                    let should_copy = files_to_copy
                        .iter()
                        .any(|pattern| match_pattern(&name, pattern, self.args.other.no_wildcards));

                    if should_copy {
                        if !apply_filters(&name, &self.args, true) {
                            continue;
                        };
                        let file = match archive.by_name(&name) {
                            Ok(f) => f,
                            Err(e) => {
                                debug!("{} not in archive: {}", name, e);
                                continue;
                            }
                        };
                        self.changed_files_total_size += file.compressed_size();
                        self.changed_files_total_count += 1;
                    }
                }
            }
            _ => {}
        }

        Ok(())
    }
}
impl<'a> Drop for RunState<'a> {
    // 实现Drop trait，用于清理临时文件
    fn drop(&mut self) {
        // 确保所有资源都已关闭
        if let Some(mut writer) = self.writer.take() {
            let _ = writer.finish();
        }

        if let Some(log_file) = self.log_file.as_mut() {
            log_file
                .log_summary(self.total_entries, self.total_original_size)
                .unwrap();
            let _ = log_file.close();
        }

        // 删除临时文件
        if let Some(tmp) = self.zip_file_tmp.as_ref() {
            if tmp.exists() {
                fs::remove_file(tmp).unwrap();
            }
        }
    }
}

// 定义 trait 来统一不同数据类型的接口
pub trait SizeProvider {
    fn get_size(&self) -> u64;
}

// 为 PathBuf 实现 SizeProvider
impl SizeProvider for &PathBuf {
    fn get_size(&self) -> u64 {
        self.metadata()
            .map(|m| if self.is_dir() { 0 } else { m.len() })
            .unwrap_or(0)
    }
}
// 为 u64 实现 SizeProvider
impl SizeProvider for u32 {
    fn get_size(&self) -> u64 {
        *self as u64
    }
}
// 为 u64 实现 SizeProvider
impl SizeProvider for u64 {
    fn get_size(&self) -> u64 {
        *self
    }
}

// 初始化运行状态
pub fn init_run_state(args: &crate::cli::ZipArgs) -> anyhow::Result<RunState> {
    let mut state = RunState::new(args.zipfile.clone());

    // 先设置调试标志，这样后续的调试输出才能生效
    state.show_debug = args.other.show_debug;

    // 输出调试信息
    if let Some(zipfile) = &args.zipfile {
        state.debug_print(&format!("Zipfile name '{}'", zipfile.display()));
    }
    state.debug_print("Command line read");

    state.debug_print("Open zip file and create temp file");

    // 修复：正确处理 -b 参数指定的临时目录
    let output_path = if let Some(temp_path) = &args.other.temp_path {
        // 如果指定了临时路径，在该目录下创建随机临时文件（类似标准zip工具）
        let temp_filename = generate_temp_filename();
        let temp_file_path = temp_path.join(temp_filename);

        // 确保临时目录存在
        if !temp_path.exists() {
            std::fs::create_dir_all(temp_path)?;
        }

        // 更新state中的临时文件路径
        state.zip_file_tmp = Some(temp_file_path.clone());

        temp_file_path.to_str().unwrap().to_string()
    } else {
        // 在zip文件所在目录创建随机临时文件（类似标准zip工具）
        let temp_filename = generate_temp_filename();
        let temp_dir = state
            .zip_file
            .as_ref()
            .and_then(|p| p.parent())
            .unwrap_or_else(|| Path::new("."));
        let temp_file_path = temp_dir.join(temp_filename);

        // 更新state中的临时文件路径
        state.zip_file_tmp = Some(temp_file_path.clone());

        temp_file_path.to_str().unwrap().to_string()
    };

    let mut writer = ZipWriter::new(&output_path)?;

    if let Some(split_size) = args.split.split_size {
        if args.split.split_verbose {
            log::info!("split_verbose is enabled");
            println!("splitsize = {}", split_size);
        }

        writer.set_split_args(
            args.split.split_size,
            args.split.split_beep,
            args.split.split_verbose,
        )?;

        // 修复分割文件命名：始终基于最终的zip文件名而不是临时文件名
        let final_output_path = if let Some(output) = &args.other.out {
            output.to_str().unwrap()
        } else {
            state.zip_file.as_ref().unwrap().to_str().unwrap()
        };
        writer.update_base_name_for_output(final_output_path);

        // 使用ask_user_for_split_path作为split_path_callback
        if args.split.split_pause {
            log::info!("split_pause is enabled, will ask user for split path");
            writer.set_split_callback(ask_user_for_split_path);
        }
    };

    if state.zip_file.as_ref().map_or(false, |p| p.exists()) {
        // 如果文件存在，打开它
        state.debug_print("Reading archive");
        let archive = ZipArchive::new(
            state
                .zip_file
                .as_ref()
                .unwrap()
                .to_str()
                .ok_or_else(|| anyhow::anyhow!("Invalid UTF-8 in file path"))?,
        )?;

        // 复制现有的注释
        let comments = archive.comment().to_string();
        writer.set_archive_comment(&comments);

        state.archive = Some(archive);
        state.debug_print("Going through old zip file");
    } else {
        state.debug_print("Creating new zip file");
    }
    state.writer = Some(writer);

    state.update_modify_time = args.basic_options.latest_time;
    state.testing = args.test.test;

    let file_option = get_file_options(args)?;
    state.file_options = file_option;

    state.verbose = args.basic_options.verbose;
    state.quiet = args.basic_options.quiet;

    state.output = args.other.out.clone();
    if args.logging.logfile.is_some() {
        let mut log_file = LogFile::new(
            args.logging.logfile.clone().unwrap(),
            args.logging.logfile_append,
            args.logging.logfile_info,
        );

        log_file.log_command(&std::env::args().collect::<Vec<_>>())?;
        state.log_file = Some(log_file);
    }

    state.set_display_info(args);

    state.args = args.clone();

    Ok(state)
}

#[allow(clippy::write_literal)]
#[allow(dead_code)]
pub fn init_logger() {
    // 初始化日志
    env_logger::Builder::from_env(Env::default().default_filter_or("off"))
        .format(|buf, record| {
            use std::io::Write;
            let level_style = buf.default_level_style(record.level());
            writeln!(
                buf,
                "[{} {}{}{} {}:{}] {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                level_style, // 直接使用level_style
                record.level(),
                "\x1b[0m", // 重置颜色
                record.file().unwrap_or("unknown"),
                record.line().unwrap_or(0),
                record.args()
            )
        })
        .init();
}

#[allow(clippy::write_literal)]
#[allow(dead_code)]
pub fn init_logger_with_level(level: log::LevelFilter) {
    // 初始化日志
    env_logger::Builder::from_env(Env::default().default_filter_or(level.to_string()))
        .format(|buf, record| {
            use std::io::Write;
            let level_style = buf.default_level_style(record.level());
            writeln!(
                buf,
                "[{} {}{}{} {}:{}] {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                level_style, // 直接使用level_style
                record.level(),
                "\x1b[0m", // 重置颜色
                record.file().unwrap_or("unknown"),
                record.line().unwrap_or(0),
                record.args()
            )
        })
        .init();
}

fn ask_user_for_split_path(index: u16) -> anyhow::Result<PathBuf> {
    use std::io::{self, Write};
    use std::sync::OnceLock;

    // 使用OnceLock存储上次的有效路径
    static LAST_PATH: OnceLock<PathBuf> = OnceLock::new();

    println!("\nOpening disk {}", index);
    println!("Hit ENTER to write to default path of");
    // 如果有上次的路径，显示上次的路径提示
    if let Some(last_path) = LAST_PATH.get() {
        println!("  {}", last_path.display());
    } else {
        println!("  (current directory)");
    }
    println!("or enter a new directory path (. for cur dir) and hit ENTER");

    let mut split_path: Option<PathBuf> = None;
    let mut input;
    loop {
        print!("\nPath (or hit ENTER to continue): ");
        io::stdout().flush()?;

        input = String::new();
        io::stdin().read_line(&mut input)?;

        if input.trim().is_empty() {
            break;
        }

        let trimmed_input = input.trim().to_string();
        let path_buf = PathBuf::from(trimmed_input);

        // 根据是否以斜杠结尾决定返回路径还是父路径
        split_path = if path_buf == PathBuf::from(".") {
            Some(PathBuf::from("."))
        } else if input.trim().ends_with('/') {
            Some(path_buf)
        } else {
            Some(path_buf.parent().unwrap_or(Path::new("/")).to_path_buf())
        };

        if let Some(ref path) = split_path {
            let _ = LAST_PATH.set(path.clone());
        }

        println!("Writing to:");
        println!(
            "  {}",
            if split_path
                .as_ref()
                .unwrap()
                .display()
                .to_string()
                .is_empty()
            {
                "(current directory)".to_string()
            } else {
                split_path.as_ref().unwrap().display().to_string()
            }
        );
    }

    let result_path = split_path.unwrap_or_else(|| {
        LAST_PATH
            .get()
            .cloned()
            .unwrap_or_else(|| PathBuf::from("."))
    });
    Ok(result_path)
}

pub fn collect_files(
    path: &Path,
    files: &mut Vec<PathBuf>,
    recurse: bool,
    _no_wildcards_boundary: bool,
    allow_filesystem_wildcards: bool,
) -> Result<()> {
    // 遵循原生zip的逻辑：
    // 1. 如果路径存在（文件或目录），直接处理
    // 2. 如果路径不存在，尝试通配符展开
    // 3. 如果通配符展开也没有匹配到任何文件，返回特殊错误

    if path.exists() {
        // 路径存在，直接处理
        if path.is_file() {
            files.push(path.to_path_buf());
            return Ok(());
        } else if path.is_dir() {
            // 处理目录（递归或非递归）
            return handle_directory(path, files, recurse);
        }
    }

    // 路径不存在，如果允许则尝试通配符展开
    let path_str = path.to_string_lossy();
    if allow_filesystem_wildcards && (path_str.contains('*') || path_str.contains('?')) {
        debug!("Attempting wildcard expansion for: {}", path_str);
        let mut matched_any = false;

        match glob(&path_str) {
            Ok(paths) => {
                for entry in paths {
                    match entry {
                        Ok(matched_path) => {
                            matched_any = true;
                            debug!("Wildcard matched: {}", matched_path.display());

                            if matched_path.is_file() {
                                files.push(matched_path);
                            } else if matched_path.is_dir() {
                                handle_directory(&matched_path, files, recurse)?;
                            }
                        }
                        Err(e) => {
                            debug!("Glob error for {}: {}", path_str, e);
                        }
                    }
                }
            }
            Err(e) => {
                debug!("Glob pattern error for {}: {}", path_str, e);
            }
        }

        if matched_any {
            return Ok(());
        }
    }

    // 路径不存在且通配符展开也没有匹配到任何文件
    // 原生zip的行为取决于命令类型：
    // - Add模式：显示"name not matched"警告
    // - Update/Delete模式：在zip文件现有条目中搜索匹配的模式
    // 这里我们返回一个特殊的错误，让各个命令自己处理
    Err(ZipError::InvalidArguments(format!("file_not_found: {}", path.display())).into())
}

// 处理目录的辅助函数
fn handle_directory(path: &Path, files: &mut Vec<PathBuf>, recurse: bool) -> Result<()> {
    if recurse {
        // 递归模式使用walkdir，同时记录目录和文件
        for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
            let entry_path = entry.path();
            let path = if entry_path.is_dir() {
                if entry_path.to_string_lossy().ends_with('/') {
                    entry_path.to_string_lossy().into_owned()
                } else {
                    format!("{}/", entry_path.to_string_lossy())
                }
            } else {
                entry_path.to_string_lossy().into_owned()
            };
            files.push(path.into());
        }
    } else {
        // 非递归模式直接读取目录
        files.push(path.to_path_buf());
    }
    Ok(())
}

// 辅助函数：在第一个通配符处分割路径
#[allow(dead_code)]
fn split_path_at_first_wildcard(path: &Path) -> Result<(PathBuf, String)> {
    let mut base = PathBuf::new();
    let mut pattern = String::new();
    let mut found_wildcard = false;

    for component in path.components() {
        let comp_str = component.as_os_str().to_string_lossy();
        if !found_wildcard && (comp_str.contains('*') || comp_str.contains('?')) {
            found_wildcard = true;
            pattern.push_str(&comp_str);
        } else if found_wildcard {
            pattern.push('/');
            pattern.push_str(&comp_str);
        } else {
            base.push(component);
        }
    }

    if !found_wildcard {
        return Err(
            ZipError::InvalidArguments("Path does not contain wildcards".to_string()).into(),
        );
    }
    // 如果base为空，则设置为当前目录
    if base.as_os_str().is_empty() {
        base.push(".");
    }
    Ok((base, pattern))
}

// 获取路径 <name:path>
pub fn prepare_files_map(
    args: &crate::cli::ZipArgs,
    _command: &crate::cli::Command,
    temp_file_path: Option<&PathBuf>,
) -> Result<std::collections::BTreeMap<String, PathBuf>> {
    let files = args.files.clone();
    let recurse = args.basic_options.recurse;
    let junk_paths = args.basic_options.junk_paths;
    let no_dirs = args.other.no_dir_entries;

    // 预计算固定路径，避免在循环中重复计算
    let zip_file_abs = args
        .zipfile
        .as_ref()
        .map(|zip_file| fs::canonicalize(zip_file).unwrap_or(zip_file.clone()));
    let temp_file_abs =
        temp_file_path.map(|temp_path| fs::canonicalize(temp_path).unwrap_or(temp_path.clone()));
    let base_dir_zip_abs = args
        .other
        .temp_path
        .as_ref()
        .and_then(|base_dir| {
            args.zipfile.as_ref().map(|zip_file| {
                let base_zip = base_dir.join(zip_file.file_name().unwrap());
                fs::canonicalize(&base_zip).ok()
            })
        })
        .flatten();

    // 当使用 -R/--recurse-patterns 标志时，patterns 位于 args.files 中。
    // 该模式下的行为应与原生 zip 保持一致：
    //   1. 递归遍历当前目录 (不依赖 -r)
    //   2. 使用提供的多个通配符模式进行匹配
    if args.other.recurse_patterns {
        let mut files_map: std::collections::BTreeMap<String, PathBuf> =
            std::collections::BTreeMap::new();

        // 如果未提供任何 pattern，视为参数错误
        if args.files.is_empty() {
            return Err(
                ZipError::InvalidArguments("no patterns specified for -R".to_string()).into(),
            );
        }

        for pattern_path in &args.files {
            let raw_pattern = pattern_path.to_string_lossy();
            let regex_pattern = raw_pattern
                .replace('.', "\\.")
                .replace('*', ".*")
                .replace('?', ".");

            let re = match regex::Regex::new(&format!("^{}$", regex_pattern)) {
                Ok(r) => r,
                Err(e) => {
                    log::warn!("Invalid pattern '{}' : {}", raw_pattern, e);
                    continue;
                }
            };

            for entry in WalkDir::new(".").into_iter().filter_map(|e| e.ok()) {
                if !entry.file_type().is_file() {
                    continue;
                }
                let file_name = entry.path().to_string_lossy().to_string();
                if apply_filters(&file_name, args, false) && re.is_match(&file_name) {
                    files_map.insert(file_name.replace("./", ""), entry.path().to_path_buf());
                }
            }
        }

        log::debug!("files_map (recurse-patterns): {:?}", files_map);
        return Ok(files_map);
    }

    let mut files_map: std::collections::BTreeMap<String, PathBuf> =
        std::collections::BTreeMap::new();
    if !files.is_empty() {
        let mut files_to_check: Vec<PathBuf> = Vec::new();
        let mut archive_search_patterns: Vec<String> = Vec::new();

        for file_path in files {
            // 新的逻辑：根据原生zip的行为，带引号的通配符永远不进行文件系统展开
            // 如果参数包含通配符字符（*、?），说明它是带引号传入的，不应该进行文件系统展开
            let path_str = file_path.to_string_lossy();
            let has_wildcards = path_str.contains('*') || path_str.contains('?');
            let allow_filesystem_wildcards = !has_wildcards;

            match collect_files(
                &file_path,
                &mut files_to_check,
                recurse,
                args.other.no_wildcards_boundary,
                allow_filesystem_wildcards,
            ) {
                Ok(()) => {}
                Err(e) => {
                    // 检查是否是文件不存在的情况
                    if e.to_string().contains("file_not_found:") {
                        // 如果文件包含通配符且zip文件存在，需要在归档中搜索模式
                        let zip_exists = args.zipfile.as_ref().map_or(false, |zf| zf.exists());
                        if has_wildcards && zip_exists {
                            archive_search_patterns.push(file_path.to_string_lossy().to_string());
                        } else {
                            // 其他情况显示name not matched警告
                            if !args.basic_options.quiet {
                                println!(
                                    "        utzip warning: name not matched: {}",
                                    file_path.display()
                                );
                            }
                        }
                    } else if e.to_string().contains("name not matched") {
                        if !args.basic_options.quiet {
                            println!(
                                "        utzip warning: name not matched: {}",
                                file_path.display()
                            );
                        }
                    } else {
                        return Err(e);
                    }
                }
            }
        }

        // 如果有归档搜索模式，这种情况需要在各个命令中单独处理
        if !archive_search_patterns.is_empty() {
            // 返回第一个需要在归档中搜索的模式的错误
            return Err(ZipError::InvalidArguments(format!(
                "file_not_found: {}",
                archive_search_patterns[0]
            ))
            .into());
        }

        for file_path in &files_to_check {
            // 处理路径前缀和后缀
            let mut file_name = if junk_paths {
                file_path
                    .file_name()
                    .unwrap()
                    .to_string_lossy()
                    .into_owned()
            } else {
                // 去除 ./ 或 / 前缀
                let path_str = file_path.to_string_lossy();
                path_str
                    .trim_start_matches("./")
                    .trim_start_matches('/')
                    .to_string()
            };

            // 跳过输出zip文件和中间文件（使用预计算的路径）
            if let Some(zip_file_abs_ref) = &zip_file_abs {
                // 优化：只对需要比较的文件调用canonicalize，并缓存结果
                let file_path_abs = if file_path.is_absolute() {
                    file_path.clone()
                } else {
                    fs::canonicalize(file_path).unwrap_or(file_path.clone())
                };

                // 检查是否是实际的临时文件
                let mut should_skip = file_path_abs == *zip_file_abs_ref;

                if let Some(temp_file_abs_ref) = &temp_file_abs {
                    should_skip = should_skip || file_path_abs == *temp_file_abs_ref;
                }

                // -b参数指定的目录下的zip文件
                if let Some(base_dir_zip_abs_ref) = &base_dir_zip_abs {
                    should_skip = should_skip || file_path_abs == *base_dir_zip_abs_ref;
                }

                if should_skip {
                    log::debug!("skipping output zip file itself: {}", file_path.display());
                    continue;
                }
            }

            if no_dirs && file_path.is_dir() {
                log::debug!("skipping directory: {}", file_path.display());
                continue;
            }

            // 如果是目录则添加/后缀
            if file_path.is_dir() && !file_name.ends_with('/') {
                file_name.push('/');
            }

            if junk_paths && files_map.contains_key(&file_name) {
                let info = format!(
                    "utzip warning:    first full name: {}
            second full name: {}
            name in zip file repeated: {}
            this may be a result of using -j",
                    files_map.get(&file_name).unwrap().display(),
                    file_path.display(),
                    file_name
                );
                if !args.basic_options.quiet {
                    println!("{}\n", info);
                }
                return Err(ZipError::DuplicateFileName(
                    "cannot repeat names in zip file".to_string(),
                )
                .into());
            }
            files_map.insert(file_name, file_path.clone());
        }
    }

    log::debug!("files_map: {:?}", files_map);
    Ok(files_map)
}

// 复制本地文件或目录到zip
pub fn copy_local_file_to_zip(
    run_state: &mut RunState,
    zip_writer: &mut ZipWriter,
    file_path: &PathBuf,
    name: String,
) -> Result<FileCompressionTracker> {
    // 检查是否是特殊标记路径，这种路径不应该传递到这个函数
    let file_path_str = file_path.to_string_lossy();
    if file_path_str.starts_with("__ZIP_ENTRY__:") {
        return Err(anyhow::anyhow!(
            "Invalid path: special marker path should not be passed to copy_local_file_to_zip: {}",
            file_path_str
        ));
    }

    run_state.total_entries += 1;

    // 处理符号链接
    if run_state.file_options.store_symlinks && file_path.is_symlink() {
        log::debug!("adding symlink: {}", name);
        let link_target = std::fs::read_link(file_path)?;
        let mut options = run_state.file_options.clone();

        // 设置符号链接属性 (Unix 符号链接模式)
        options.set_file_path(file_path)?;
        options.external_attr = 0xA1FF0000;
        options.with_compression(CompressionMethod::Stored);

        zip_writer.start_file(&name, options)?;
        zip_writer.write_all(link_target.to_string_lossy().as_bytes())?;

        let compress_info = zip_writer.finish_file()?;

        // 基于压缩后大小更新点显示
        update_dots_display(run_state, compress_info.compressed_size)?;

        return Ok(FileCompressionTracker {
            original_size: link_target.to_string_lossy().len() as u64,
            compressed_size: link_target.to_string_lossy().len() as u64,
            ratio: 0.0,
            method: CompressionMethod::Stored,
            disk_num: zip_writer.get_current_split_index(),
        });
    }

    // 处理目录
    if file_path.is_dir() {
        log::debug!("adding directory: {}", name);
        let mut options = run_state.file_options.clone();
        options.set_file_path(file_path)?;

        zip_writer.add_directory(name, options)?;
        return Ok(FileCompressionTracker {
            original_size: 0,
            compressed_size: 0,
            ratio: 0.0,
            method: CompressionMethod::Stored,
            disk_num: zip_writer.get_current_split_index(),
        });
    }

    let mut file = File::open(file_path)?;
    let mut buffer = Box::new([0u8; 32 * 1024]); // 32kB buffer

    let is_txt = is_text_file(file_path);
    log::debug!("is_txt: {}", is_txt);
    let convert_type = run_state.file_options.get_line_ending_conversion(is_txt);
    log::debug!("convert_type: {:?}", convert_type);

    let mut options = run_state.file_options.clone();
    options.set_file_path(file_path)?;

    zip_writer.start_file(&name, options)?;
    file.seek(std::io::SeekFrom::Start(0))?;

    let mut previous_compressed_bytes = 0u64;
    let mut file_compressed_total = 0u64; // 当前文件的总压缩字节数
    let mut file_original_total = 0u64; // 当前文件的总原始字节数
    let mut file_dots_shown = 0u64; // 当前文件已显示的点数
    let dot_size = run_state
        .args
        .display
        .display_dots_size
        .unwrap_or(10 * 1024 * 1024);

    loop {
        let bytes_read = file.read(&mut *buffer)?;
        if bytes_read == 0 {
            break;
        }

        file_original_total += bytes_read as u64;

        if is_txt {
            // 处理文本文件的行结束符转换
            let mut converted_buffer = Vec::with_capacity(bytes_read * 2);
            convert_line_endings(&buffer[..bytes_read], convert_type, &mut converted_buffer);
            zip_writer.write_all(&converted_buffer)?;
        } else {
            // 直接写入二进制数据
            zip_writer.write_all(&buffer[..bytes_read])?;
        }

        // 基于不同选项显示进度点
        let current_compressed_bytes = zip_writer.get_current_file_compressed_bytes();
        let compressed_increment = if current_compressed_bytes >= previous_compressed_bytes {
            current_compressed_bytes - previous_compressed_bytes
        } else {
            // 处理分割文件时压缩字节数重置的情况
            current_compressed_bytes
        };

        if compressed_increment > 0 {
            file_compressed_total += compressed_increment;
        }

        // 更新点显示
        update_dots_display_mixed(
            run_state,
            bytes_read as u64,    // 原始数据增量
            compressed_increment, // 压缩数据增量
            &mut file_dots_shown,
            file_original_total,   // 当前文件原始总大小
            file_compressed_total, // 当前文件压缩总大小
            dot_size,
        )?;

        previous_compressed_bytes = current_compressed_bytes;
    }

    let compress_info = zip_writer.finish_file()?;

    Ok(FileCompressionTracker {
        original_size: compress_info.original_size,
        compressed_size: compress_info.compressed_size,
        method: compress_info.method,
        ratio: caculate_ratio(compress_info.original_size, compress_info.compressed_size),
        disk_num: zip_writer.get_current_split_index(),
    })
}

// 基于压缩后大小更新点显示（用于文件完成后）
fn update_dots_display(run_state: &mut RunState, compressed_bytes: u64) -> Result<()> {
    let dot_size = run_state
        .args
        .display
        .display_dots_size
        .unwrap_or(10 * 1024 * 1024);

    if run_state.display_dots {
        // --dd: 按每个文件的压缩大小显示点（这里用于符号链接等简单情况）
        let dots = compressed_bytes / dot_size;
        for _ in 0..dots {
            print!(".");
        }
        if dots > 0 {
            std::io::stdout().flush()?;
        }
    }

    if run_state.display_global_dots {
        // --dg: 按全局累计压缩大小显示点
        let old_global_bytes = run_state.global_bytes_processed;
        run_state.global_bytes_processed += compressed_bytes;

        let old_global_dots = old_global_bytes / dot_size;
        let new_global_dots = run_state.global_bytes_processed / dot_size;

        if new_global_dots > old_global_dots {
            for _ in old_global_dots..new_global_dots {
                print!(".");
            }
            std::io::stdout().flush()?;
        }
    }

    Ok(())
}

// 混合点显示逻辑：--dd使用原始大小，--dg使用压缩大小
fn update_dots_display_mixed(
    run_state: &mut RunState,
    _original_increment: u64,  // 原始数据增量
    compressed_increment: u64, // 压缩数据增量
    file_dots_shown: &mut u64,
    file_original_total: u64,    // 当前文件原始总大小
    _file_compressed_total: u64, // 当前文件压缩总大小
    dot_size: u64,
) -> Result<()> {
    if run_state.display_dots {
        // --dd: 按每个文件的原始大小显示点
        let total_file_dots = file_original_total / dot_size;

        // 显示新增的点
        if total_file_dots > *file_dots_shown {
            let new_dots = total_file_dots.saturating_sub(*file_dots_shown);
            for _ in 0..new_dots {
                print!(".");
            }
            *file_dots_shown = total_file_dots;
            std::io::stdout().flush()?;
        }
    }

    if run_state.display_global_dots {
        // --dg: 按全局累计压缩大小显示点
        if compressed_increment > 0 {
            let old_global_bytes = run_state.global_bytes_processed;
            run_state.global_bytes_processed += compressed_increment;

            let old_global_dots = old_global_bytes / dot_size;
            let new_global_dots = run_state.global_bytes_processed / dot_size;

            if new_global_dots > old_global_dots {
                for _ in old_global_dots..new_global_dots {
                    print!(".");
                }
                std::io::stdout().flush()?;
            }
        }
    }

    // 如果两个选项都没启用，仍需要更新全局字节数以便其他功能使用
    // 这里使用压缩后的大小作为统计
    if !run_state.display_dots && !run_state.display_global_dots {
        run_state.global_bytes_processed += compressed_increment;
    }

    Ok(())
}

// 计算压缩比
pub fn caculate_ratio(original_size: u64, compressed_size: u64) -> f64 {
    if original_size > 0 {
        if compressed_size >= original_size {
            // 压缩无效或者压缩后反而变大，显示0%（与原生zip一致）
            0.0
        } else {
            (1.0 - (compressed_size as f64 / original_size as f64)) * 100.0
        }
    } else {
        0.0
    }
}

// 复制已有的zip文件到zip
pub fn copy_zip_file_to_zip(
    run_state: &mut RunState,
    source: &ZipFile,
    target: &mut ZipWriter,
) -> Result<()> {
    if source.encrypted() {
        debug!("is encrypted: {}", source.name());
    }
    run_state.total_entries += 1;
    run_state.total_original_size += source.origin_size();
    run_state.total_compressed_size += source.compressed_size();

    // 整体二进制进行拷贝
    target.raw_copy_file(source, source.name())?;

    Ok(())
}

// 获取输入密码
#[allow(dead_code)]
pub fn prompt_password() -> Result<String> {
    // 测试环境下自动使用环境变量密码
    if let Ok(pwd) = std::env::var("UTZIP_TEST_PASSWORD") {
        return Ok(pwd);
    }

    print!("Enter passwd: ");
    std::io::stdout().flush()?;
    let password = read_password()?;

    Ok(password)
}

pub fn prompt_and_verify_password() -> Result<String> {
    // 测试环境下自动使用环境变量密码
    if let Ok(pwd) = std::env::var("UTZIP_TEST_PASSWORD") {
        return Ok(pwd);
    }

    print!("Enter passwd: ");
    std::io::stdout().flush()?;
    let password = read_password()?;

    print!("Verify passwd: ");
    std::io::stdout().flush()?;
    let confirm = read_password()?;

    if password != confirm {
        Err(anyhow::anyhow!("password verification failed"))
    } else {
        Ok(password)
    }
}

pub fn get_file_options(args: &crate::cli::ZipArgs) -> Result<FileOptions> {
    let mut options = FileOptions::new();

    let compression_method = if args.compression.store_only {
        CompressionMethod::Stored
    } else if let Some(method) = &args.compression.compression_method {
        match method.as_str() {
            "deflate" => CompressionMethod::Deflated,
            "bzip2" => CompressionMethod::Bzip2,
            "store" => CompressionMethod::Stored,
            _ => {
                return Err(
                    ZipError::InvalidArguments("Invalid compression method".to_string()).into(),
                )
            }
        }
    } else {
        CompressionMethod::Deflated
    };

    // 处理行结束符转换
    options.convert_lf_to_crlf = args.translation.convert_lf_to_crlf;
    options.convert_crlf_to_lf = args.translation.convert_crlf_to_lf;

    // 处理压缩级别，并区分是否由用户明确指定
    let (level, user_specified) = match (
        args.compression.store_only,
        args.compression.compress_faster,
        args.compression.compress_better,
        args.compression.level_2,
        args.compression.level_3,
        args.compression.level_4,
        args.compression.level_5,
        args.compression.level_6,
        args.compression.level_7,
        args.compression.level_8,
    ) {
        (true, _, _, _, _, _, _, _, _, _) => (0, true), // -0 显式指定
        (_, true, _, _, _, _, _, _, _, _) => (1, true), // -1 显式指定
        (_, _, true, _, _, _, _, _, _, _) => (9, true), // -9 显式指定
        (_, _, _, true, _, _, _, _, _, _) => (2, true), // -2 显式指定
        (_, _, _, _, true, _, _, _, _, _) => (3, true), // -3 显式指定
        (_, _, _, _, _, true, _, _, _, _) => (4, true), // -4 显式指定
        (_, _, _, _, _, _, true, _, _, _) => (5, true), // -5 显式指定
        (_, _, _, _, _, _, _, true, _, _) => (6, true), // -6 显式指定
        (_, _, _, _, _, _, _, _, true, _) => (7, true), // -7 显式指定
        (_, _, _, _, _, _, _, _, _, true) => (8, true), // -8 显式指定
        _ => (6, false),                                // 默认值，非用户指定
    };

    options.with_compression(compression_method);

    // 只有在用户明确指定时才调用with_compression_level，否则保持默认值供自动优化
    if user_specified {
        options.with_compression_level(level);
    } else {
        // 用户未指定，使用默认值但不标记为已指定，允许后续自动优化
        options.compression_level = level;
    }

    // 密码设置
    {
        if let Some(pwd) = &args.encryption.password {
            options.with_password(pwd);
        }
        if args.encryption.encrypt {
            let pwd = prompt_and_verify_password()?;
            options.with_password(&pwd);
        }
    }

    options.no_extra_field = args.other.no_extra;

    options.store_symlinks = args.other.store_symlinks;

    //-n
    if let Some(dont_compress_suffixes) = &args.other.dont_compress_suffixes {
        options.no_compress_extensions.extend(
            dont_compress_suffixes
                .split(':')
                .filter(|s| !s.is_empty()) // 过滤掉空字符串
                .map(|s| s.to_string()),
        );
    }

    Ok(options)
}

// 设置文件的注释
pub fn set_file_comment(run_state: &mut RunState) -> Result<()> {
    let mut writer = run_state.writer.take().unwrap();
    let headers = writer.central_directory_headers_mut();
    for header in headers {
        // 修改单个文件 header 中的注释
        let name = header.get_file_name();
        if run_state.changed_files.contains(&name) {
            let comment = get_input_comment(&name)?;
            log::debug!("Setting comment for {}: {}", name, comment);
            header.set_comment(&comment);
        }
    }
    run_state.writer = Some(writer);

    Ok(())
}

// 获取用户输入的文件注释
fn get_input_comment(name: &String) -> Result<String> {
    println!("Enter comment for {} (press Enter to skip):", name);

    get_input(true, false)
}

// 获取用户输入的归档注释
pub fn get_input_archive_comment() -> Result<String> {
    println!("Enter new zip file comment (end with .):");
    get_input(false, true)
}

// 获取用户输入
fn get_input(enter_break: bool, point_break: bool) -> Result<String> {
    let mut comment = String::new();
    if std::io::stdin().is_terminal() {
        let mut line = String::new();
        loop {
            line.clear();

            std::io::stdin().read_line(&mut line)?;

            // 严格匹配：整行只能是 . 且无前后空格
            if point_break && line.trim() == "." {
                break;
            }

            if enter_break && line.ends_with('\n') {
                comment.push_str(&line);
                break;
            }

            // 保留原始输入格式（包括换行符和空格）
            comment.push_str(&line);
        }
    } else {
        // 输入流不是终端，尝试从 /dev/tty 读取
        log::warn!("Reading from /dev/tty...");

        if let Ok(tty) = std::fs::File::open("/dev/tty") {
            let mut tty = std::io::BufReader::new(tty);
            let mut line = String::new();
            loop {
                line.clear();
                tty.read_line(&mut line)?;

                if point_break && line.trim() == "." {
                    break;
                }

                if enter_break && line.ends_with('\n') {
                    comment.push_str(&line);
                    break;
                }

                comment.push_str(&line);
            }
        }
    }

    Ok(comment.trim().to_string())
}

// 获取文件的修改时间并转换为ZIP格式时间戳
pub fn get_file_modification_time(file_path: &Path) -> anyhow::Result<(u16, u16)> {
    let metadata = std::fs::metadata(file_path)?;
    let modified = metadata.modified()?;

    // 将SystemTime转换为chrono::DateTime
    let modified = chrono::DateTime::<chrono::Local>::from(modified);

    // 转换为ZIP格式时间戳
    let time = ((modified.hour() as u16) << 11)    // 小时占5位(11-15)
             | ((modified.minute() as u16) << 5)   // 分钟占6位(5-10)
             | ((modified.second() as u16) >> 1); // 秒/2占5位(0-4)

    let date = (((modified.year() - 1980) as u16) << 9)  // 年从1980开始，占7位(9-15)
             | ((modified.month() as u16) << 5)          // 月占4位(5-8)
             | (modified.day() as u16); // 日占5位(0-4)

    Ok((time, date))
}

// 新增行结束符转换函数
fn convert_line_endings(data: &[u8], convert_type: LineEndingConversion, output: &mut Vec<u8>) {
    match convert_type {
        LineEndingConversion::LfToCrlf => {
            // 只将裸LF（未被CR前缀的LF）转换为CRLF
            let mut i = 0;
            while i < data.len() {
                if data[i] == b'\n' {
                    if i == 0 || data[i - 1] != b'\r' {
                        output.push(b'\r');
                    }
                    output.push(b'\n');
                    i += 1;
                } else {
                    output.push(data[i]);
                    i += 1;
                }
            }
        }
        LineEndingConversion::CrlfToLf => {
            let mut i = 0;
            while i < data.len() {
                if i + 1 < data.len() && data[i] == b'\r' && data[i + 1] == b'\n' {
                    output.push(b'\n');
                    i += 2;
                } else {
                    output.push(data[i]);
                    i += 1;
                }
            }
        }
        LineEndingConversion::None => {
            output.extend_from_slice(data);
        }
    }
}

// 判断文件是否为文本文件
fn is_text_file(file_path: &Path) -> bool {
    use std::io::Read;

    if let Ok(mut file) = File::open(file_path) {
        let mut buffer = [0u8; 1024];
        if let Ok(bytes_read) = file.read(&mut buffer) {
            let slice = &buffer[..bytes_read];

            // 1. 优先尝试UTF-8解码
            if let Ok(s) = std::str::from_utf8(slice) {
                // 检查是否包含大量不可见控制字符（允许常见的换行、回车、制表符）
                let ctrl_count = s
                    .chars()
                    .filter(|&c| {
                        (c < '\x20' && c != '\n' && c != '\r' && c != '\t') || c == '\u{7f}'
                    })
                    .count();
                // 如果控制字符比例很低，认为是文本
                return ctrl_count * 5 < s.len().max(1);
            }

            // 2. 回退到ASCII判断
            let ascii_text = slice.iter().all(|&b| {
                (b.is_ascii_graphic() || b.is_ascii_whitespace())
                // 允许常见换行、回车、制表符
                || b == b'\n' || b == b'\r' || b == b'\t'
            });
            return ascii_text;
        }
    }
    false
}

// 简单的模式匹配函数
pub fn match_pattern(name: &str, pattern: &str, no_wildcards: bool) -> bool {
    if no_wildcards {
        return name == pattern;
    }
    // 简单实现，支持 * 和 ? 通配符
    let pattern = pattern.replace('*', ".*").replace('?', ".");
    match regex::Regex::new(&format!("^{}$", pattern)) {
        Ok(regex) => regex.is_match(name),
        Err(e) => {
            warn!("Invalid pattern '{}': {}", pattern, e);
            false
        }
    }
}

/// 根据操作模式应用不同的筛选逻辑
pub fn apply_filters(name: &str, args: &crate::cli::ZipArgs, is_archive_file: bool) -> bool {
    // 删除操作只能使用排除筛选
    if args.command == crate::cli::Command::Delete && !args.filter.include.is_empty() {
        log::warn!(
            "The delete operation cannot use the -i option, inclusion filtering has been ignored"
        );
    }

    // 内部操作(删除/复制)的筛选逻辑
    if is_archive_file {
        // 删除操作只能使用-x
        if args.command == crate::cli::Command::Delete {
            return args
                .filter
                .exclude
                .iter()
                .any(|p| match_pattern(name, p, args.other.no_wildcards));
        }
        // 复制操作可以使用-i和-x
        else if args.command == cli::Command::Copy {
            let included = args.filter.include.is_empty()
                || args
                    .filter
                    .include
                    .iter()
                    .any(|p| match_pattern(name, p, args.other.no_wildcards));
            let excluded = args
                .filter
                .exclude
                .iter()
                .any(|p| match_pattern(name, p, args.other.no_wildcards));
            return included && !excluded;
        }
    }
    // 外部操作(添加/更新)的筛选逻辑
    else {
        // 包含逻辑
        let included = if args.filter.include.is_empty() {
            true
        } else {
            args.filter.include.iter().any(|p| {
                if args.other.no_wildcards_boundary && (p.contains('*') || p.contains('?')) {
                    // only match same directory depth as pattern
                    let pattern_slashes = p.matches('/').count();
                    let name_slashes = name.matches('/').count();
                    if name_slashes == pattern_slashes {
                        match_pattern(name, p, args.other.no_wildcards)
                    } else {
                        false
                    }
                } else {
                    match_pattern(name, p, args.other.no_wildcards)
                }
            })
        };
        // 排除逻辑
        let excluded = args.filter.exclude.iter().any(|p| {
            if args.other.no_wildcards_boundary && (p.contains('*') || p.contains('?')) {
                let pattern_slashes = p.matches('/').count();
                let name_slashes = name.matches('/').count();
                if name_slashes == pattern_slashes {
                    match_pattern(name, p, args.other.no_wildcards)
                } else {
                    false
                }
            } else {
                match_pattern(name, p, args.other.no_wildcards)
            }
        });
        return included && !excluded;
    }

    true
}

// 筛选输入的文件
pub fn filter_filesystem_files(
    file_selects: &std::collections::BTreeMap<String, PathBuf>,
    args: &crate::cli::ZipArgs,
) -> std::collections::BTreeMap<String, PathBuf> {
    let mut filtered_files = std::collections::BTreeMap::new();

    for (name, path) in file_selects {
        // 对于zip条目路径，跳过日期过滤
        let path_str = path.to_string_lossy();
        if path_str.starts_with("__ZIP_ENTRY__:") {
            // zip条目直接包含，不进行文件系统相关的过滤
            filtered_files.insert(name.clone(), path.clone());
            continue;
        }

        if !should_include_file(name, path, args) {
            log::debug!("skipping: {}", name);
            continue;
        }

        if should_log_inclusion(args) {
            println!("including: {}", name);
        }
        filtered_files.insert(name.clone(), path.clone());
    }
    filtered_files
}

fn should_include_file(name: &str, path: &Path, args: &crate::cli::ZipArgs) -> bool {
    // 文件名筛选
    if !apply_filters(name, args, false) {
        if should_log_exclusion(args) {
            println!("excluding: {}", name);
        }
        return false;
    }

    // 日期筛选
    let modified = match get_file_modification_date(path) {
        Some(date) => date,
        None => return false, // 无法获取修改时间的文件排除
    };

    match (&args.data_filter.after_date, &args.data_filter.before_date) {
        (Some(after), None) => check_after_date(name, modified, after, args),
        (None, Some(before)) => check_before_date(name, modified, before, args),
        (Some(after), Some(before)) => check_date_range(name, modified, after, before, args),
        (None, None) => true, // 没有日期限制
    }
}

fn get_file_modification_date(path: &Path) -> Option<chrono::NaiveDate> {
    path.metadata()
        .and_then(|m| m.modified())
        .map(|t| chrono::DateTime::<chrono::Local>::from(t).date_naive())
        .ok()
}

fn check_after_date(
    name: &str,
    modified: chrono::NaiveDate,
    after: &chrono::NaiveDate,
    args: &crate::cli::ZipArgs,
) -> bool {
    if modified < *after {
        log_exclusion(name, "missing or early", args);
        false
    } else {
        true
    }
}

fn check_before_date(
    name: &str,
    modified: chrono::NaiveDate,
    before: &chrono::NaiveDate,
    args: &crate::cli::ZipArgs,
) -> bool {
    if modified >= *before {
        log_exclusion(name, "missing or early", args);
        false
    } else {
        true
    }
}

fn check_date_range(
    name: &str,
    modified: chrono::NaiveDate,
    after: &chrono::NaiveDate,
    before: &chrono::NaiveDate,
    args: &crate::cli::ZipArgs,
) -> bool {
    if modified < *after || modified >= *before {
        log::debug!(
            "zip diagnostic: {} not in range [{}, {})",
            name,
            after,
            before
        );
        log_exclusion(name, "missing or early", args);
        false
    } else {
        true
    }
}

fn log_exclusion(name: &str, reason: &str, args: &crate::cli::ZipArgs) {
    if !args.basic_options.quiet && args.basic_options.verbose {
        println!("zip diagnostic: {} {}", name, reason);
    }
}

fn should_log_inclusion(args: &crate::cli::ZipArgs) -> bool {
    !args.basic_options.quiet
        && args.basic_options.verbose
        && (!args.filter.exclude.is_empty() || !args.filter.include.is_empty())
}

fn should_log_exclusion(args: &crate::cli::ZipArgs) -> bool {
    !args.basic_options.quiet
        && args.basic_options.verbose
        && (!args.filter.exclude.is_empty() || !args.filter.include.is_empty())
}

// 使用指定的命令测试zip文件是否有效
pub fn test_zip_with_command(command: &str, zip_path: &Path) -> Result<()> {
    let parts: Vec<&str> = command.split_whitespace().collect();
    let mut cmd = std::process::Command::new(parts[0]);
    if parts.len() > 1 {
        cmd.args(&parts[1..]);
    }
    let status = cmd.arg(zip_path.to_str().unwrap()).status()?;

    if !status.success() {
        return Err(ZipError::UnzipError(
            "Zip file invalid, could not spawn unzip, or wrong unzip (original files unmodified)"
                .to_string(),
        )
        .into());
    }
    Ok(())
}

// 使用unzip命令测试zip文件是否有效，verbose为true时，输出详细信息
pub fn test_zip_with_unzip(zip_path: &Path, verbose: bool) -> Result<()> {
    let unzip_command = if verbose { "unzip -t" } else { "unzip -tqq" };
    test_zip_with_command(unzip_command, zip_path)
}

// 判断是否需要更新文件
// 通过比较本地文件的修改时间和压缩包中的文件的修改时间来判断
pub fn should_update_file(file: &ZipFile, file_path: &Path) -> Result<bool> {
    // 检查是否是特殊标记路径
    let file_path_str = file_path.to_string_lossy();
    if file_path_str.starts_with("__ZIP_ENTRY__:") {
        return Err(anyhow::anyhow!(
            "Invalid path: special marker path should not be passed to should_update_file: {}",
            file_path_str
        ));
    }

    let local_metadata = fs::metadata(file_path)?;
    let local_modified = chrono::DateTime::<chrono::Local>::from(local_metadata.modified()?);
    let archive_modified = file.last_modified()?;

    let diff = local_modified - archive_modified;

    let local_size = if file_path.is_dir() {
        0
    } else {
        local_metadata.len()
    };

    // 奇数秒会被向下取整到最近的偶数秒,所有 ZIP 工具都遵循这个规范
    let update = diff.num_seconds() > 2 || local_size != file.origin_size();
    Ok(update)
}

// 来格式化大小, ext 保留几位小数
fn format_size(size: u64, ext: u8) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;

    if size >= GB {
        format!("{:.*}G", ext as usize, size as f64 / GB as f64)
    } else if size >= MB {
        format!("{:.*}M", ext as usize, size as f64 / MB as f64)
    } else if size >= KB {
        format!("{:.*}K", ext as usize, size as f64 / KB as f64)
    } else {
        format!("{}", size)
    }
}

// 在zip归档中搜索匹配模式的条目
pub fn search_pattern_in_archive(
    run_state: &mut RunState,
    pattern: &str,
    args: &crate::cli::ZipArgs,
) -> Result<std::collections::BTreeMap<String, PathBuf>> {
    debug!("Searching pattern in archive: {}", pattern);
    let mut matched_files = std::collections::BTreeMap::new();

    if let Some(archive) = run_state.archive.as_ref() {
        // 构建正则表达式模式
        let regex_pattern = if args.other.no_wildcards_boundary {
            // 不跨目录边界：将*替换为[^/]*
            pattern.replace('*', "[^/]*").replace('?', "[^/]")
        } else {
            // 跨目录边界：原生zip的行为是*可以匹配任何字符包括/
            pattern.replace('*', ".*").replace('?', ".")
        };

        let regex = regex::Regex::new(&format!("^{}$", regex_pattern))?;

        // 在zip文件的现有条目中搜索匹配的模式
        for i in 0..archive.len() {
            let file = archive.by_index_raw(i)?;
            let name = file.name().to_string();

            if regex.is_match(&name) {
                debug!("Found matching entry in archive: {}", name);

                // 检查文件系统中是否存在对应的文件
                if std::path::Path::new(&name).exists() {
                    // 只有文件系统中也存在的文件才会被处理，符合原生zip的行为
                    matched_files.insert(
                        name.clone(),
                        PathBuf::from(format!("__ZIP_ENTRY__:{}", name)),
                    );
                } else {
                    debug!(
                        "File {} exists in archive but not in filesystem, skipping",
                        name
                    );
                }
            }
        }
    }

    // 如果没有匹配到任何条目，显示警告
    if matched_files.is_empty() {
        if !args.basic_options.quiet {
            println!("        utzip warning: name not matched: {}", pattern);
        }
    } else {
        debug!(
            "Found {} matching entries that exist in both archive and filesystem",
            matched_files.len()
        );
    }

    Ok(matched_files)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;
    use tempfile::{tempdir, NamedTempFile};

    #[test]
    fn test_collect_files_with_direct_file() {
        let file = NamedTempFile::new().unwrap();
        let mut files = Vec::new();
        let result = collect_files(file.path(), &mut files, false, false, true);

        assert!(result.is_ok());
        assert_eq!(files.len(), 1);
        assert_eq!(files[0], file.path());
    }

    #[test]
    fn test_collect_files_with_wildcard_no_boundary() {
        let dir = tempdir().unwrap();
        let file1 = tempfile::Builder::new()
            .prefix("test_")
            .tempfile_in(dir.path())
            .unwrap();
        let file2 = tempfile::Builder::new()
            .prefix("test_")
            .tempfile_in(dir.path())
            .unwrap();

        let pattern = dir.path().join("test_*");
        let mut files = Vec::new();
        let result = collect_files(&pattern, &mut files, false, true, true);

        assert!(result.is_ok());
        assert!(files.iter().any(|p| p == file1.path()));
        assert!(files.iter().any(|p| p == file2.path()));
    }

    #[test]
    fn test_collect_files_with_wildcard_cross_boundary() {
        let dir = tempdir().unwrap();
        let subdir = tempfile::Builder::new()
            .prefix("subdir_")
            .tempdir_in(dir.path())
            .unwrap();

        let file1 = tempfile::Builder::new()
            .suffix(".tmp")
            .tempfile_in(dir.path())
            .unwrap();
        let file2 = tempfile::Builder::new()
            .suffix(".tmp")
            .tempfile_in(subdir.path())
            .unwrap();

        let pattern = dir.path().join("*/*.tmp");
        let mut files = Vec::new();
        let result = collect_files(&pattern, &mut files, false, false, true);

        assert!(result.is_ok());
        assert!(!files.iter().any(|p| p == file1.path()));
        assert!(files.iter().any(|p| p == file2.path()));
    }

    #[test]
    fn test_collect_files_with_directory_non_recursive() {
        let dir = tempdir().unwrap();
        let mut files = Vec::new();
        let result = collect_files(dir.path(), &mut files, false, false, true);

        assert!(result.is_ok());
        assert_eq!(files.len(), 1);
        assert_eq!(files[0], dir.path());
    }

    #[test]
    fn test_collect_files_with_directory_recursive() {
        let dir = tempdir().unwrap();
        let subdir = tempfile::Builder::new().tempdir_in(dir.path()).unwrap();
        let file = NamedTempFile::new_in(dir.path()).unwrap();

        let mut files = Vec::new();
        let result = collect_files(dir.path(), &mut files, true, false, true);

        assert!(result.is_ok());
        assert!(files.iter().any(|p| p == file.path()));
        assert!(files.iter().any(|p| p == subdir.path()));
    }

    #[test]
    fn test_collect_files_with_invalid_path() {
        let invalid_path = PathBuf::from("/nonexistent/path");
        let mut files = Vec::new();
        let result = collect_files(&invalid_path, &mut files, false, false, true);

        assert!(result.is_err());
    }
}
